﻿// Autore: Alessio Parma

using System;
using System.Collections.Generic;
using System.Data.Objects.DataClasses;
using System.Linq;

namespace RecipesManager.Data.Implementation
{
    sealed partial class Guest : IGuest
    {
        readonly ILink<ICategory> _categoryIntolerances;
        readonly IRatings<ICategory, FoodRating> _categoryRatings;
        readonly IRatings<IGuest, GuestRating> _guestRatings;
        readonly ILink<IGuest> _guestRelationships;
        readonly ILink<IIngredient> _ingredientIntolerances;
        readonly IRatings<IIngredient, FoodRating> _ingredientRatings;
        readonly IRatings<IRecipe, FoodRating> _recipeRatings;
        readonly IRecipeStats _recipeStats;

        public Guest()
        {
            _categoryIntolerances = new CategoryEntities(() => CategoryIntolerances);
            _categoryRatings = new CategoryRatings(() => CategoryRatings, () => Id);
            _ingredientIntolerances = new IngredientEntities(() => IngredientIntolerances);
            _ingredientRatings = new IngredientRatings(() => IngredientRatings, () => Id);
            _guestRatings = new OtherGuestRatings(() => GuestRatings, () => Id);
            _guestRelationships = new GuestEntities(() => GuestRelationships, () => this);
            _recipeRatings = new RecipeRatings(() => RecipeRatings, () => Id);
            _recipeStats = new RecipeStats(() => RecipesStats, () => Id);
        }

        #region IGuest Members

        string INamedEntity.Name
        {
            get { return Name; }
        }

        ILink<ICategory> IGuest.CategoryIntolerances
        {
            get { return _categoryIntolerances; }
        }

        IRatings<ICategory, FoodRating> IGuest.CategoryRatings
        {
            get { return _categoryRatings; }
        }

        IEnumerable<IEvent> IGuest.EventsParticipations
        {
            get { return EventsParticipations.Where(ep => ep.HasParticipated).Select(ep => ep.Event); }
        }

        ILink<IIngredient> IGuest.IngredientIntolerances
        {
            get { return _ingredientIntolerances; }
        }

        IRatings<IIngredient, FoodRating> IGuest.IngredientRatings
        {
            get { return _ingredientRatings; }
        }

        IRatings<IGuest, GuestRating> IGuest.GuestRatings
        {
            get { return _guestRatings; }
        }

        ILink<IGuest> IGuest.GuestRelationships
        {
            get { return _guestRelationships; }
        }

        IEnumerable<IOtherRating<IGuest, GuestRating>> IGuest.OtherGuestRatings
        {
            get { return OtherGuestRatings; }
        }

        IRatings<IRecipe, FoodRating> IGuest.RecipeRatings
        {
            get { return _recipeRatings; }
        }

        IRecipeStats IGuest.RecipeStats
        {
            get { return _recipeStats; }
        }

        public bool Equals(IGuest other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return Id == other.Id;
        }

        public void Update(string name, string notes)
        {
            Name = name.ToKey();
            Notes = notes;
            Manager.SaveChanges();
        }

        #endregion

        public static Guest New(string name, string notes)
        {
            name = name.ToKey();
            return CreateGuest(0, name, notes);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            return ReferenceEquals(this, obj) || Equals(obj as IGuest);
        }

        public override int GetHashCode()
        {
            return Id;
        }

        public override string ToString()
        {
            return string.Format("Guest(Name: {0})", Name);
        }
    }

    sealed class GuestObjects : NamedObjects<Guest, IGuest>, IGuests
    {
        public GuestObjects() : base(Manager.GuestSet)
        {
        }

        #region IGuests Members

        public IGuest Add(string name, string notes = "")
        {
            return Add(Guest.New(name, notes));
        }

        #endregion
    }

    sealed class GuestEntities : NamedEntities<Guest, IGuest>, ILink<IGuest>
    {
        readonly Func<Guest> _guestGetter;

        public GuestEntities(Func<EntityCollection<Guest>> items, Func<Guest> guestGetter)
            : base(items, Manager.GuestSet)
        {
            _guestGetter = guestGetter;
        }

        #region ILink<IGuest> Members

        public INamedEntities<IGuest> Entities
        {
            get { return Manager.Guests; }
        }

        public IGuest Add(string name)
        {
            var g = (Guest) Manager.Guests[name];
            g.GuestRelationships.Add(_guestGetter());
            return AddName(name);
        }

        public override void Remove(int id)
        {
            var g = (Guest) Manager.Guests[id];
            g.GuestRelationships.Remove(_guestGetter());
            base.Remove(id);
        }

        public override void Remove(string name)
        {
            var g = (Guest) Manager.Guests[name];
            g.GuestRelationships.Remove(_guestGetter());
            base.Remove(name);
        }

        #endregion
    }

    sealed partial class OtherGuestRating : IRating<IGuest, GuestRating>, IOtherRating<IGuest, GuestRating>
    {
        #region IOtherRating<IGuest,GuestRating> Members

        IGuest IOtherRating<IGuest, GuestRating>.Entity
        {
            get { return Guest; }
        }

        int IOtherRating<IGuest, GuestRating>.Id
        {
            get { return GuestId; }
        }

        string IOtherRating<IGuest, GuestRating>.Name
        {
            get { return Guest.Name; }
        }

        GuestRating IOtherRating<IGuest, GuestRating>.Rating
        {
            get { return (GuestRating) Rating; }
        }

        #endregion

        #region IRating<IGuest,GuestRating> Members

        IGuest IRating<IGuest, GuestRating>.Entity
        {
            get { return OtherGuest; }
        }

        int IEntity.Id
        {
            get { return OtherGuestId; }
        }

        string INamedEntity.Name
        {
            get { return OtherGuest.Name; }
        }

        GuestRating IRating<IGuest, GuestRating>.Rating
        {
            get { return (GuestRating) Rating; }
        }

        public void Update(GuestRating rating)
        {
            Rating = (byte) rating;
            Manager.SaveChanges();
        }

        #endregion
    }

    sealed class OtherGuestRatings : NamedLinkSet<OtherGuestRating, IRating<IGuest, GuestRating>>,
                                     IRatings<IGuest, GuestRating>
    {
        public OtherGuestRatings(Func<EntityCollection<OtherGuestRating>> items, Func<int> idGetter)
            : base(items, idGetter)
        {
        }

        #region IRatings<IGuest,GuestRating> Members

        public INamedEntities<IGuest> Entities
        {
            get { return Manager.Guests; }
        }

        public IRating<IGuest, GuestRating> Add(string name, GuestRating rating)
        {
            return Add(OtherGuestRating.CreateOtherGuestRating(IdGetter(), Entities[name].Id, (byte) rating));
        }

        #endregion
    }

    sealed partial class EventGuestParticipation : IGuestParticipation
    {
        #region IGuestParticipation Members

        IGuest IGuestParticipation.Guest
        {
            get { return Guest; }
        }

        bool IGuestParticipation.HasParticipated
        {
            get { return HasParticipated; }
        }

        int IEntity.Id
        {
            get { return GuestId; }
        }

        string INamedEntity.Name
        {
            get { return Guest.Name; }
        }

        public void Update(bool partecipated)
        {
            HasParticipated = partecipated;
            Manager.SaveChanges();
        }

        #endregion

        public static EventGuestParticipation New(int eventId, string guestName)
        {
            return CreateEventGuestParticipation(eventId, Manager.Guests[guestName].Id);
        }
    }

    sealed class GuestParticipations : NamedLinkSet<EventGuestParticipation, IGuestParticipation>, IGuestParticipations
    {
        public GuestParticipations(Func<EntityCollection<EventGuestParticipation>> items, Func<int> idGetter)
            : base(items, idGetter)
        {
        }

        #region IGuestParticipations Members

        public IGuestParticipation Add(string guestName)
        {
            return Add(EventGuestParticipation.New(IdGetter(), guestName));
        }

        #endregion
    }
}